
// Droplets Renderer
/**
  * Created on 02/20/2012
  * By Anshul Kanakia
  *
  */
#include "OGLDataObjects.h"
#include "OGLScene.h"
#include "OpenGLFunctions.h"

#include <gl/glew.h>
#include <glm/glm.hpp>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#define _USE_MATH_DEFINES
#include <cmath>
#include <sstream>

#define VERTEX_SHADER_FILE "SimpleVertexShader.glsl"
#define FRAGMENT_SHADER_FILE "SimpleFragmentShader.glsl"

#ifdef _LAB
const char *shaderDirectory = "E:\\Documents\\My Dropbox\\Work\\Grad_School\\Lab\\Droplets\\DropletRenderer\\Shaders\\";
#else	// _HOME
const char *shaderDirectory = "G:\\Documents\\My Dropbox\\Work\\Grad_School\\Lab\\Droplets\\DropletRenderer\\Shaders\\";
#endif

// Globals
typedef struct
{
	float XYZW[4];
	float RGBA[4];
} Vertex;

extern OGLSchene *_oglSchene;

// An array of vertices to render a simple polygon
Vertex dropletVertexData[] = 
{
	// Center of GL_TRIANGLE_FAN - (x, y, z, w) with muted blue color
	{{0.0f, 0.0f, 1.0f, 1.0f},		{0.0f, 0.0f, 1.0f, 1.0f}},
	{{0.0f, 1.0f, 0.1f, 1.0f},		{0.8f, 0.8f, 1.0f, 1.0f}},
	{{0.71f, 0.71f, 0.1f, 1.0f},	{0.8f, 0.8f, 1.0f, 1.0f}},
	{{1.0f, 0.0f, 0.1f, 1.0f},		{0.8f, 0.8f, 1.0f, 1.0f}},
	{{0.71f, -0.71f, 0.1f, 1.0f},	{0.8f, 0.8f, 1.0f, 1.0f}},
	{{0.0f, -1.0f, 0.1f, 1.0f},		{0.8f, 0.8f, 1.0f, 1.0f}},
	{{-0.71f, -0.71f, 0.1f, 1.0f},	{0.8f, 0.8f, 1.0f, 1.0f}},
	{{-1.0f, 0.0f, 0.1f, 1.0f},		{0.8f, 0.8f, 1.0f, 1.0f}},
	{{-0.71f, 0.71f, 0.1f, 1.0f},	{0.8f, 0.8f, 1.0f, 1.0f}},
	{{0.0f, 1.0f, 0.1f, 1.0f},		{0.8f, 0.8f, 1.0f, 1.0f}}
};

Vertex floorVertexData[] =
{
	// Center of GL_TRIANGLE_STRIP - (x, y, z, w) with muted grey color
	{{0.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f, 1.0f}},
	{{0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.1f, 0.0f, 1.0f}},
	{{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.1f, 1.0f}},
	{{1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}
};

int main()
{
	// Initialize the OGLSchene object 
	_oglSchene = new OGLSchene();	// Use Defaults Constructor

	// Initializes an OpenGL Window and Glew functionality
	if(_oglSchene->InitializeSchene(WINDOW_TITLE) != OGL_SUCCESS)
	{
		delete _oglSchene;
		exit(EXIT_FAILURE);
	}

	// Set the callback functions
	glfwSetWindowSizeCallback(OGLWindowResizeCallback);

	// Create Vertex Array Objects and Vertex Buffer Objects
	// Create a Vertex Array Object to store state data
	GLuint vaoid, vboid;
	if(OGLCreateVertexArrayObject(&vaoid) != OGL_SUCCESS)
	{
		glfwTerminate();
		delete _oglSchene;
		exit(EXIT_FAILURE);
	}
	_oglSchene->AddVAOID("VAO", vaoid);

	// 2. Create Vertex Buffer Objects
	const int numDropletVertices = 10;
	const int numFloorVertices = 4;
	const size_t dropletBufferSize = sizeof(dropletVertexData);
	const size_t floorBufferSize = sizeof(floorVertexData);
	const size_t vertexSize = sizeof(dropletVertexData[0]);
	const size_t rgbOffset = sizeof(dropletVertexData[0].XYZW);

	const float numRows = 2.0f;
	const float colStep = 10.0f;
	float colStart = -20.0f;
	float rLoc;

	// I) Create a floor
	OGLDataObject *flrObj = InitializeOGLObjects(1);
	flrObj->objType = GL_ARRAY_BUFFER;
	flrObj->usageType = GL_STATIC_DRAW;
	flrObj->vecLength = 4; // (x,y,z,w)
	flrObj->dataType = GL_FLOAT;
	flrObj->normalized = GL_FALSE;
	flrObj->stride = 
	// Add geomData data
	flrObj->geomData = new OGLGeometryData(glm::vec3(-25.0f, -25.0f, 0.0f));	// Center at (-25,-25,0)
	flrObj->geomData->SetScaleMat(glm::vec3(50.0f, 50.0f, 1.0f));	// Make it large
	flrObj->geomData->drawType = GL_TRIANGLE_STRIP;
	flrObj->geomData->vertexCount = numFloorVertices;
	if(OGLCreateVertexBufferObject(
			&floorVertexData[0], 
			floorBufferSize, 
			flrObj->objType, 
			flrObj->usageType, 
			&vboid) != OGL_SUCCESS)
	{
		glfwTerminate();
		DestroyOGLObjects(flrObj, 1);
		delete _oglSchene;
		exit(EXIT_FAILURE);
	}
	flrObj->vboID = vboid;

	// Add Vertex Array Index data to OGLflrObject
	flrObj->vaPointerIndex->clear();
	// *** Create Vertex Attribute Pointers AFTER the new buffer object is bound! ***
	// Make sure the attribute pointer ids match up with what is stored in OGLDataObject::vaPointerIndex
	glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(floorVertexData[0]), 0);
	glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(floorVertexData[0]), 
		(GLvoid*)sizeof(floorVertexData[0].XYZW));
	flrObj->vaPointerIndex->push_back(1);
	flrObj->vaPointerIndex->push_back(2);
		
	// Add OGLflrObject to the schene
	if(_oglSchene->AddObjectBufferData("VBOFloor", flrObj, 1) != OGL_SUCCESS)
	{
		glfwTerminate();
		DestroyOGLObjects(flrObj, 1);
		delete _oglSchene;
		exit(EXIT_FAILURE);
	}

	// **************************************************************************
	// Create Droplets
	// II) Create the Droplets
	for(int i = 0; i < NUM_ROBOTS; i++)
	{
		std::string vboName = "VBOn";
		
		if(i == NUM_ROBOTS / 2)
			colStart = -20.0f;
		if(i < (NUM_ROBOTS / 2))
			rLoc = -10.0f;
		else
			rLoc = 10.0f;
		
		colStart += colStep;
		// Initiate OGLDataObject and add type data
		OGLDataObject *obj = InitializeOGLObjects(1);
		obj->objType = GL_ARRAY_BUFFER;
		obj->usageType = GL_STATIC_DRAW;
		// Add geomData data
		obj->geomData = new OGLGeometryData(glm::vec3(colStart, rLoc, 0.0f));
		obj->geomData->drawType = GL_TRIANGLE_FAN;
		obj->geomData->vertexCount = numDropletVertices;
		// Create the Vertex Buffer Object and add its ID to OGLDataObject
		if(OGLCreateVertexBufferObject(
			&dropletVertexData[0], 
			dropletBufferSize, 
			obj->objType, 
			obj->usageType, 
			&vboid) != OGL_SUCCESS)
		{
			glfwTerminate();
			DestroyOGLObjects(obj, 1);
			delete _oglSchene;
			exit(EXIT_FAILURE);
		}
		obj->vboID = vboid;

		// Add Vertex Array Index data to OGLDataObject
		obj->vaPointerIndex->clear();	
		// *** Create Vertex Attribute Pointers AFTER the new buffer object is bound! ***
		// Make sure the attribute pointer ids match up with what is stored in OGLDataObject::vaPointerIndex
		glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, vertexSize, 0);
		glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, vertexSize, (GLvoid*)rgbOffset);
		obj->vaPointerIndex->push_back(1);
		obj->vaPointerIndex->push_back(2);
		
		// Add OGLDataObject to the schene
		if(_oglSchene->AddObjectBufferData(
			vboName.replace(3, 1, IntToString(i+1)), 
			obj, 1) != OGL_SUCCESS)
		{
			glfwTerminate();
			DestroyOGLObjects(obj, 1);
			delete _oglSchene;
			exit(EXIT_FAILURE);
		}
	}
	

	// Load Shaders
	std::string shaderPath = shaderDirectory;
	std::vector<GLuint> shaderIDs;
	shaderIDs.clear();
	shaderPath += VERTEX_SHADER_FILE;
	shaderIDs.push_back(OGLLoadShader(shaderPath.c_str(), GL_VERTEX_SHADER));	// Load vertex shader
	shaderPath.replace(std::strlen(shaderDirectory), strlen(VERTEX_SHADER_FILE), FRAGMENT_SHADER_FILE);
	shaderIDs.push_back(OGLLoadShader(shaderPath.c_str(), GL_FRAGMENT_SHADER));
	
	// Load the Program
	_oglSchene->AddProgramID("GLSLProgram", OGLLoadProgram(shaderIDs));	// Load the shader program

	// Create the projection and view matrices
	_oglSchene->SetScheneParams();
	_oglSchene->SetCamera();

	// Get a handle for the MVP matrix in our GLSL program
	GLuint pid;
	if(_oglSchene->GetProgramID("GLSLProgram", &pid) == OGL_SUCCESS)
	{	
		GLuint matrixID = glGetUniformLocation(pid, "MVP");
		_oglSchene->AddMatrixID("MVP", matrixID);
	}

	// Enter the main loop
	OGLMainLoop();

	// Cleanup
	_oglSchene->DestroyScheneData();


	return 0;
}